// SQLiteMap.cc
// author: dipina@eside.deusto.es
// compile: g++ SQLiteMap.cc -lsqlite3 -o SQLiteMap
#include "SQLiteMap.h"

namespace PracticaCaso
{
	// Constructor. Calls to loadMappings(fileName).
	SQLiteMap::SQLiteMap(string fn) 
	    : fileName(fn)
	    , dbh(0)
	    , dns2IpPortMap()
	{
		// Process the contents of the mapping file
		this->loadMappings(fn);
	}

	// Constructor. Calls to loadMappings(fileName).
	SQLiteMap::SQLiteMap(const SQLiteMap& rhs)
	{
		fileName = rhs.fileName;
		dbh = rhs.dbh;
		this->loadMappings(fileName);
	}

	SQLiteMap::~SQLiteMap() {
		cout << "SQLiteMap: destructor called " << endl;
		this->close();
	}

	// loadMappings(fileName). Loads the fileName DB to dns2IpPortMap.
	void SQLiteMap::loadMappings(string mappingsDBFileName)
	{
		cout << "Opening database ..." << endl;
		if (sqlite3_open(mappingsDBFileName.c_str(), &dbh) != SQLITE_OK)
		{
			cerr << "Can't open database: " << sqlite3_errmsg(dbh) << endl;
			this->close();
		}
		
		char **result;
		int nrow, ncol;
		char *errorMsg;
		cout << "Checking if table already exists ..." << endl;
		if (sqlite3_get_table(dbh, "select * from KeyValuePair", &result, &nrow, &ncol, &errorMsg) != SQLITE_OK) 
		{
	  		cerr << errorMsg << endl;
	  		sqlite3_free(errorMsg);
	  	
	  		if (sqlite3_get_table(dbh, "create table KeyValuePair(key_element BLOB NOT NULL PRIMARY KEY, value_element BLOB)", &result, &nrow, &ncol, &errorMsg) != SQLITE_OK)
	  		{
		  		cerr << errorMsg << endl;
		  		sqlite3_free(errorMsg);
		  		this->close();
	  		}
	  		
	  		cout << "Table KeyValuePair created" << endl;
	  		sqlite3_free_table(result);
	  		
		} 
		else
		{
	
			cout << "Loading contents of database into cache ..." << endl;
			
				if (nrow == 0 && ncol == 0) 
				{
					sqlite3_free_table(result);
				}	
				else
				{
					// sqlite3 returns one extra row with the column headers
					for (int i=0; i <= nrow; i++) 
					{
						dns2IpPortMap[result[i*ncol]] = result[i*ncol + 1]; 
					}
		
					sqlite3_free_table(result);
					cout << "Closing database ..." << endl;
				}
		}	
	}

	// getMap(): returns dns2IpPortMap
	map<string, string> SQLiteMap::getMap()
	{
		return  dns2IpPortMap;
	}

	// get(key): returns the value corresponding to key from dns2IpPortMap
	string SQLiteMap::get(string key)
	{
		return dns2IpPortMap[key];
	}
	// set (key, value) (implemented by us)
	void SQLiteMap::set(string mapKey, string mapValue) 
	{
		// Undertake the update of the STL map and the database. Bear in mind that it there is not an entry with a passed key an INSERT will have to be executed, if there was already such an entry an UPDATE will take place
		cout << "Entra en SQLiteMap::set.";
		string aux = get(mapKey);
		
		if(aux.empty()) 
		{
			cout << "No esta ese valor en BD --> INSERT.";
			//Insertar en la BD el dato
			char **result;
			int nrow;
			int ncol;
			char *errorMsg;
			string comotuquieras = "insert into KeyValuePair(key_element, value_element) values('" + mapKey + "', '" + mapValue + "')";
			cout << comotuquieras;
			
			if (sqlite3_get_table(dbh, comotuquieras.c_str(), &result, &nrow, &ncol, &errorMsg) != SQLITE_OK) 
			{
			cerr << errorMsg << endl;
			sqlite3_free(errorMsg);
			this->close();
			} 
			else 
			{
			cout << "Row created: " << nrow << endl;
			sqlite3_free_table(result);
			}
		}	
		else
		{
			//Actualizar en la BD el dato
			char **result;
			int nrow;
			int ncol;
			char *errorMsg;
			string aux2  = "UPDATE KeyValuePair SET value_element='" + mapValue +  "' WHERE key_element='" + mapKey + "'";
			
			if (sqlite3_get_table(dbh, aux2.c_str(), &result, &nrow, &ncol, &errorMsg) != SQLITE_OK) 
			{
				cerr << errorMsg << endl;
				sqlite3_free(errorMsg);
				this->close();
			} 
			else 
			{
				cout << "Row updated: " << nrow << endl;
				sqlite3_free_table(result);
			}
		}
		
		// Actualizar la memoria
		dns2IpPortMap[mapKey] = mapValue;
}		

	// close DB
	void SQLiteMap::close() 
	{
		sqlite3_close(dbh);
		exit(1);
	}

	ostream & operator << (ostream & os, SQLiteMap &t) {
		os << "DB file name: " << t.fileName << endl;
		os << "DNS mappings:" << endl;
		typedef map<string, string>::const_iterator CI;
		for (CI p = t.dns2IpPortMap.begin(); p != t.dns2IpPortMap.end(); ++p) {
			os <<  p->first << " : " << p->second << endl;
		}
		return os;
	}
}


/*
// global variable
PracticaCaso::SQLiteMap * SQLiteMap_pointer;

// function called when CTRL-C is pressed
void ctrl_c(int)
{
    printf("\nCTRL-C was pressed...\n");
	delete SQLiteMap_pointer;
}


void usage() {
	cout << "Usage: SQLiteMap <name-mappings-db-file>" << endl;
	exit(1);
}

int main(int argc, char** argv) {
	signal(SIGINT,ctrl_c);

	if (argc != 2) {
		usage();
	}

	PracticaCaso::SQLiteMap * SQLiteMap = new PracticaCaso::SQLiteMap((string)argv[1]);
	cout << "SQLiteMap instance: " << endl << SQLiteMap << endl;
	//SQLiteMap_pointer = &SQLiteMap;
	SQLiteMap_pointer = SQLiteMap;

	//SQLiteMap.set("saludo", "hola");
	//SQLiteMap.set("despedida", "adios");
	SQLiteMap->set("saludo", "hola");
	SQLiteMap->set("despedida", "adios");

	//cout << "SQLiteMap[\"saludo\"] = " << SQLiteMap.get("saludo") << endl;
	//cout << "SQLiteMap[\"despedida\"] = " << SQLiteMap.get("despedida") << endl;
	cout << "SQLiteMap[\"saludo\"] = " << SQLiteMap->get("saludo") << endl;
	cout << "SQLiteMap[\"despedida\"] = " << SQLiteMap->get("despedida") << endl;

	cout << "SQLiteMap instance after 2 sets: " << endl << *SQLiteMap << endl;

	//map<string, string> savedSQLiteMap = SQLiteMap.getMap();
	map<string, string> savedSQLiteMap = SQLiteMap->getMap();
	cout << "Recuperado el mapa" << endl;
	typedef map<string, string>::const_iterator CI;
	for (CI p = savedSQLiteMap.begin(); p != savedSQLiteMap.end(); ++p) {
		cout << "dns2IpPortMap[" << p->first << "] = " << p->second << endl;
	}
	
	delete SQLiteMap_pointer;
}
*/

